home *** CD-ROM | disk | FTP | other *** search
- /* last - display login history Author: Terrence W. Holm */
-
- /* last- Display the user log-in history.
- * Last(1) searches backwards through the file of log-in
- * records (/usr/adm/wtmp), displaying the length of
- * log-in sessions as requested by the options:
- *
- * Usage: last [-r] [-count] [-f file] [name] [tty] ...
- *
- * -r Search backwards only until the last reboot
- * record.
- *
- * -count Only print out <count> records. Last(1) stops
- * when either -r or -count is satisfied, or at
- * the end of the file if neither is given.
- *
- * -f file Use "file" instead of "/usr/adm/wtmp".
- *
- * name Print records for the user "name".
- *
- * tty Print records for the terminal "tty". Actually,
- * a list of names may be given and all records
- * that match either the user or tty name are
- * printed. If no names are given then all records
- * are displayed.
- *
- * A sigquit (^\) causes last(1) to display how far it
- * has gone back in the log-in record file, it then
- * continues. This is used to check on the progress of
- * long running searches. A sigint will stop last(1).
- *
- * Author: Terrence W. Holm May 1988
- *
- * Revision:
- * Fred van Kempen, October 1989
- * -Adapted to MSS.
- * -Adapted to new utmp database.
- *
- * Fred van Kempen, December 1989
- * -Adapted to POSIX (MINIX 1.5)
- *
- * Fred van Kempen, January 1990
- * -Final edit for 1.5
- */
- #include <sys/types.h>
- #include <signal.h>
- #include <string.h>
- #include <utmp.h>
- #include <time.h>
- #include <stdlib.h>
- #include <stdio.h>
-
-
- #define FALSE 0
- #define TRUE 1
-
- #define BUFFER_SIZE 4096 /* Room for wtmp records */
- #define MAX_WTMP_COUNT ( BUFFER_SIZE / sizeof(struct utmp) )
-
- #define min( a, b ) ( (a < b) ? a : b )
- #define max( a, b ) ( (a > b) ? a : b )
-
-
- typedef struct logout { /* A logout time record */
- char line[12]; /* The terminal name */
- long time; /* The logout time */
- struct logout *next; /* Next in linked list */
- } logout;
-
-
- static char *Version = "@(#) LAST 1.6 (01/09/90)";
-
-
- /* command-line option flags */
- char boot_limit = FALSE; /* stop on latest reboot */
- char count_limit = FALSE; /* stop after print_count */
- int print_count;
- int arg_count; /* used to select specific */
- char **args; /* users and ttys */
-
- /* global variables */
- long boot_time = 0; /* Zero means no reboot yet */
- char *boot_down; /* "crash" or "down " flag */
- logout *first_link = NULL; /* List of logout times */
- int interrupt = FALSE; /* If sigint or sigquit occurs */
-
-
- /* Sigint() and Sigquit() Flag occurrence of an interrupt. */
- void Sigint(sig)
- int sig;
- {
- interrupt = SIGINT;
- }
-
-
- void Sigquit(sig)
- int sig;
- {
- interrupt = SIGQUIT;
- }
-
-
- void usage()
- {
- fprintf(stderr, "Usage: last [-r] [-count] [-f file] [name] [tty] ...\n");
- exit(-1);
- }
-
-
- /* A log-in record format file contains four types of records.
- *
- * [1] generated on a system reboot:
- *
- * line="~", name="reboot", host="", time=date()
- *
- *
- * [2] generated after a shutdown:
- *
- * line="~", name="shutdown", host="", time=date()
- *
- *
- * [3] generated on a successful login(1)
- *
- * line=ttyname(), name=cuserid(), host=, time=date()
- *
- *
- * [4] generated by init(8) on a logout
- *
- * line=ttyname(), name="", host="", time=date()
- *
- *
- * Note: This version of last(1) does not recognize the '|' and '}' time
- * change records. Last(1) pairs up line login's and logout's to
- * generate four types of output lines:
- *
- * [1] a system reboot or shutdown
- *
- * reboot ~ Mon May 16 14:16
- * shutdown ~ Mon May 16 14:15
- *
- * [2] a login with a matching logout
- *
- * edwin tty1 Thu May 26 20:05 - 20:32 (00:27)
- *
- * [3] a login followed by a reboot or shutdown
- *
- * root tty0 Mon May 16 13:57 - crash (00:19)
- * root tty1 Mon May 16 13:45 - down (00:30)
- *
- * [4] a login not followed by a logout or reboot
- *
- * terry tty0 Thu May 26 21:19 still logged in
- */
- Process(wtmp)
- struct utmp *wtmp;
- {
- logout *link;
- logout *next_link;
-
- /* suppress the job number on an "ftp" line */
- if (!strncmp(wtmp->ut_line, "ftp", 3)) strncpy(wtmp->ut_line, "ftp", 8);
-
- if (!strcmp(wtmp->ut_line, "~")) {
- /* A reboot or shutdown record */
- if (boot_limit) exit(0);
-
- if (Print_Record(wtmp)) putchar('\n');
- boot_time = wtmp->ut_time;
-
- if (!strcmp(wtmp->ut_name, "reboot"))
- boot_down = "crash";
- else
- boot_down = "down ";
-
- /* remove any logout records */
- for (link = first_link; link != NULL; link = next_link) {
- next_link = link->next;
- free(link);
- }
- first_link = NULL;
- } else if (wtmp->ut_name[0] == '\0') {
- /* A logout record */
- Record_Logout_Time(wtmp);
- } else {
- /* A login record */
- for (link = first_link; link != NULL; link = link->next)
- if (!strncmp(link->line, wtmp->ut_line, 8)) {
- /* found corresponding logout record */
- if (Print_Record(wtmp)) {
- printf("- %.5s ", ctime(&link->time) + 11);
- Print_Duration(wtmp->ut_time, link->time);
- }
- /* record login time */
- link->time = wtmp->ut_time;
- return;
- }
- /* could not find a logout record for this login tty */
- if (Print_Record(wtmp))
- if (boot_time == 0) /* still on */
- printf(" still logged in\n");
- else { /* system crashed while on */
- printf("- %s ", boot_down);
- Print_Duration(wtmp->ut_time, boot_time);
- }
- Record_Logout_Time(wtmp); /* Needed in case of 2
- * consecutive logins */
- }
- }
-
-
- /* Print_Record(wtmp) If the record was requested, then print out
- * the user name, terminal, host and time.
- */
- Print_Record(wtmp)
- struct utmp *wtmp;
- {
- int i;
- char print_flag = FALSE;
-
- /* check if we have already printed the requested number of records */
- if (count_limit && print_count == 0) exit(0);
-
- for (i = 0; i < arg_count; ++i)
- if (!strcmp(args[i], wtmp->ut_name) || !strcmp(args[i], wtmp->ut_line))
- print_flag = TRUE;
-
- if (arg_count == 0 || print_flag) {
- #ifdef RLOGIN
- printf("%-8.8s %-8.8s %-16.16s %.16s ",
- wtmp->ut_name, wtmp->ut_line, wtmp->ut_host,
- ctime(&wtmp->ut_time));
- #else
- printf("%-8.8s %-8.8s %.16s ",
- wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time));
- #endif
- --print_count;
- return(TRUE);
- }
- return(FALSE);
- }
-
-
- /* Print_Duration(from, to) Calculate and print the days and hh:mm between
- * the log-in and the log-out.
- */
- Print_Duration(from, to)
- long from;
- long to;
- {
- long delta, days, hours, minutes;
-
- delta = max(to - from, 0);
- days = delta / (24L * 60L * 60L);
- delta = delta % (24L * 60L * 60L);
- hours = delta / (60L * 60L);
- delta = delta % (60L * 60L);
- minutes = delta / 60L;
-
- if (days > 0)
- printf("(%ld+", days);
- else
- printf(" (");
-
- printf("%02ld:%02ld)\n", hours, minutes);
- }
-
-
- /* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept.
- * Each element of the list is for one terminal.
- */
- Record_Logout_Time(wtmp)
- struct utmp *wtmp;
- {
- logout *link;
-
- /* see if the terminal is already in the list */
- for (link = first_link; link != NULL; link = link->next)
- if (!strncmp(link->line, wtmp->ut_line, 8)) {
- link->time = wtmp->ut_time;
- return;
- }
- /* allocate a new logout record, for a tty not previously encountered */
- link = (logout *) malloc(sizeof(logout));
- if (link == (logout *) NULL) {
- fprintf(stderr, "last: malloc failure\n");
- exit(1);
- }
- strncpy(link->line, wtmp->ut_line, 8);
- link->time = wtmp->ut_time;
- link->next = first_link;
-
- first_link = link;
- }
-
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char *wtmp_file = WTMP;
- FILE *f;
- long size; /* Number of wtmp records in the file */
- long now; /* time */
- int wtmp_count; /* How many to read into wtmp_buffer */
- struct utmp wtmp_buffer[MAX_WTMP_COUNT];
-
- --argc;
- ++argv;
-
- while (argc > 0 && *argv[0] == '-') {
- if (!strcmp(argv[0], "-r"))
- boot_limit = TRUE;
- else if (argc > 1 && !strcmp(argv[0], "-f")) {
- wtmp_file = argv[1];
- --argc;
- ++argv;
- } else if ((print_count = atoi(argv[0] + 1)) > 0)
- count_limit = TRUE;
- else
- usage();
-
- --argc;
- ++argv;
- }
-
- arg_count = argc;
- args = argv;
-
- if ((f = fopen(wtmp_file, "r")) == (FILE *) NULL) {
- perror(wtmp_file);
- exit(1);
- }
- if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) {
- fprintf(stderr, "last: invalid wtmp file\n");
- exit(1);
- }
- if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
- signal(SIGINT, Sigint);
- signal(SIGQUIT, Sigquit);
- }
- size /= sizeof(struct utmp); /* Number of records in wtmp */
-
- if (size == 0) {
- now = time((time_t *)0);
- printf("\nwtmp begins %.16s \n", ctime(&now));
- exit(0);
- }
- while (size > 0) {
- wtmp_count = (int) min(size, MAX_WTMP_COUNT);
- size -= (long) wtmp_count;
-
- fseek(f, size * sizeof(struct utmp), 0);
-
-
- if (fread(&wtmp_buffer[0], sizeof(struct utmp), wtmp_count, f)
- != wtmp_count) {
- fprintf(stderr, "last: read error on wtmp file\n");
- exit(1);
- }
- while (--wtmp_count >= 0) {
- Process(&wtmp_buffer[wtmp_count]);
- if (interrupt) {
- printf("\ninterrupted %.16s \n",
- ctime(&wtmp_buffer[wtmp_count].ut_time));
-
- if (interrupt == SIGINT) exit(2);
-
- interrupt = FALSE;
- signal(SIGQUIT, Sigquit);
- }
- }
-
- } /* end while(size > 0) */
-
- printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time));
- exit(0);
- }
-